package org.onion.pattern.event.springboot.rocketmq.consume.distribute;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.onion.pattern.event.idempotent.EventIdempotentService;
import org.onion.pattern.event.springboot.rocketmq.annotation.RocketMQConsumerGroup;
import org.onion.pattern.event.springboot.rocketmq.annotation.RocketMQEvent;
import org.onion.pattern.event.springboot.rocketmq.annotation.RocketMQEventListener;
import org.onion.pattern.event.springboot.rocketmq.config.RocketMessageMetadata;
import org.onion.pattern.event.springboot.rocketmq.consume.RocketMQConsumerGroupUtils;
import org.onion.pattern.event.springboot.rocketmq.consume.RocketMQContextHolder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ApplicationListenerMethodAdapter;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;

/**
 * 处理了RocketMQEventListener监听的bean
 */
@Slf4j
class RocketMQListenerMethodAdapter extends ApplicationListenerMethodAdapter {

    private final RocketMQEventListener  eventListener;
    private final Method                 method;
    private final Class<?>               consumerGroupClass;
    private final RocketMQConsumerGroup  consumerGroup;
    private final ApplicationContext     applicationContext;
    private final String                 groupName;
    private       EventIdempotentService idempotentService;
    private final ResolvableType         declaredEventType;

    public RocketMQListenerMethodAdapter(String beanName, Class<?> targetClass, Method method, ApplicationContext applicationContext) {
        super(beanName, targetClass, method);

        this.consumerGroup = AnnotationUtils.findAnnotation(targetClass, RocketMQConsumerGroup.class);
        if (this.consumerGroup == null) {
            throw new IllegalStateException("【事件】No RocketMQConsumerGroup annotation found on class: " + targetClass);
        }

        RocketMQEventListener ann = AnnotatedElementUtils.findMergedAnnotation(method, RocketMQEventListener.class);
        if (ann == null) {
            throw new IllegalStateException("【事件】No ExternalEventListener annotation found on method: " + method);
        }
        this.consumerGroupClass = targetClass;
        this.eventListener = ann;
        this.method = method;
        this.applicationContext = applicationContext;
        this.groupName = RocketMQConsumerGroupUtils.getGroupName(consumerGroupClass);

        ResolvableType _declaredEventType = null;
        //事件类型
        for (Class<?> parameterType : method.getParameterTypes()) {
            RocketMQEvent annotation = AnnotationUtils.findAnnotation(parameterType, RocketMQEvent.class);
            if (annotation == null) {
                continue;
            }
            _declaredEventType = ResolvableType.forClass(parameterType);
            break;
        }
        this.declaredEventType = _declaredEventType;
    }


    @Override
    public boolean supportsEventType(ResolvableType eventType) {
        if (this.declaredEventType == null) {
            return false;
        }

        boolean b = super.supportsEventType(eventType);
        if (b) {
            return true;
        }

        if (eventType.getType() == RocketMQApplicationEvent.class || eventType.getRawClass() == RocketMQApplicationEvent.class) {
            //泛型验证类型
            ResolvableType payloadType = eventType.as(RocketMQApplicationEvent.class).getGeneric(new int[0]);
            if (declaredEventType.isAssignableFrom(payloadType)) {
                return true;
            }
            return false;
        }

        return false;
    }

    @Override
    protected Object[] resolveArguments(ApplicationEvent event) {
        if (event instanceof RocketMQApplicationEvent) {
            RocketMQApplicationEvent rocketMQApplicationEvent = (RocketMQApplicationEvent) event;
            return new Object[]{rocketMQApplicationEvent.getPayload()};
        }
        return super.resolveArguments(event);
    }

    private boolean hasIdempotent() {
        boolean idempotent = eventListener.idempotent();
        if (idempotent) {
            return true;
        }
        return consumerGroup.idempotent();
    }

    void _processEvent(ApplicationEvent event, RocketMessageMetadata metadata) {
        //将元数据放到上下文中
        RocketMQContextHolder.setMetadata(metadata);
        try {
            processEvent(event);
        } finally {
            RocketMQContextHolder.reset();
        }
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {

        RocketMQApplicationEvent rocketMQApplicationEvent = (RocketMQApplicationEvent) event;
        RocketMessageMetadata metadata = rocketMQApplicationEvent.getMetadata();

        //只要处理一样的消费组
        if (!StringUtils.endsWith(metadata.getConsumerGroup(), "%" + this.groupName)) {
            return;
        }

        if (hasIdempotent()) { //幂等处理
            if (this.idempotentService == null) {
                this.idempotentService = this.applicationContext.getBean(EventIdempotentService.class);
            }

            String eventType;
            if (declaredEventType.getRawClass() != null) {
                eventType = declaredEventType.getRawClass().getName();
            } else {
                eventType = declaredEventType.getType().getTypeName();
            }

            this.idempotentService.execute(groupName + "-" + this.method.getName(), eventType, metadata.getMessageKey(), () -> {
                _processEvent(event, metadata);
                return null;
            });
            return;
        }

        _processEvent(event, metadata);

//        if (TransactionSynchronizationManager.isSynchronizationActive() &&
//                TransactionSynchronizationManager.isActualTransactionActive()) {
//            TransactionSynchronization transactionSynchronization = createTransactionSynchronization(event);
//            TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);
//        }
//        else if (this.annotation.fallbackExecution()) {
//            if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) {
//                logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");
//            }

//        }
//        else {
//            // No transactional event execution at all
//            if (logger.isDebugEnabled()) {
//                logger.debug("No transaction is active - skipping " + event);
//            }
//        }
    }


//    private TransactionSynchronization createTransactionSynchronization(ApplicationEvent event) {
//        return new ApplicationListenerExternalAdapter.TransactionSynchronizationEventAdapter(this, event, this.annotation.phase());
//    }
//
//
//    private static class TransactionSynchronizationEventAdapter extends TransactionSynchronizationAdapter {
//
//        private final ApplicationListenerMethodAdapter listener;
//
//        private final ApplicationEvent event;
//
//        private final TransactionPhase phase;
//
//        public TransactionSynchronizationEventAdapter(ApplicationListenerMethodAdapter listener,
//                                                      ApplicationEvent event, TransactionPhase phase) {
//
//            this.listener = listener;
//            this.event = event;
//            this.phase = phase;
//        }
//
//        @Override
//        public int getOrder() {
//            return this.listener.getOrder();
//        }
//
//        @Override
//        public void beforeCommit(boolean readOnly) {
//            if (this.phase == TransactionPhase.BEFORE_COMMIT) {
//                processEvent();
//            }
//        }
//
//        @Override
//        public void afterCompletion(int status) {
//            if (this.phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) {
//                processEvent();
//            }
//            else if (this.phase == TransactionPhase.AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) {
//                processEvent();
//            }
//            else if (this.phase == TransactionPhase.AFTER_COMPLETION) {
//                processEvent();
//            }
//        }
//
//        protected void processEvent() {
//            this.listener.processEvent(this.event);
//        }
//    }

}
